Start with data containing PM2.5, anomalous AOT, smoke day, height in meters above ground level, and pressure in hPa. Data is the result of matching 72-hour trajectories initialized from HYSPLIT points in California 2020 following Brey et al. with EPA station-day-PM2.5 data and then merging with 10 km grid of AOT, mean AOT, and smoke day. Anomalous AOT is AOT - mean AOT.

Aggregation method and cutoff determine how trajectory points were matched to EPA stations.

# Choose aggregation method: nn, idw, or k (idw of knn)
agg <- "idw"

# Choose cutoff: 10, 30, 50, 80, or 100 km
cut <- 30

# Configure data frame based on the above
dat_merged1 <- dat_merged %>% filter(agg_method == agg, cutoff == cut)

Let’s start modeling

We model PM2.5
* \(PM_{2.5} \sim AOT_{anom} + AOT_{anom} \times smokeday\)
* \(PM_{2.5} \sim AOT_{anom} + AOT_{anom} \times smokeday + AOT_{anom} \times smokeday \times height\)
* \(PM_{2.5} \sim AOT_{anom} + AOT_{anom} \times smokeday + AOT_{anom} \times smokeday \times pressure\)

df <- dat_merged1
mdl0 <- lm(pm25 ~ aot_anom + aot_anom:smoke_day,
           data = df)
mdl1 <- lm(pm25 ~ aot_anom + aot_anom:smoke_day + aot_anom:smoke_day:height,
           data = df)
mdl2 <- lm(pm25 ~ aot_anom + aot_anom:smoke_day + aot_anom:smoke_day:pressure,
           data = df)

Model with just anomalous AOT and smoke day does better than models with height or pressure. Based on the last PM2.5 modeling notebook I saw, I think these R2 values are higher? All terms are significant, but effect sizes are small for terms with height or pressure (or maybe they’re not small and it’s just an artifact of units measuring height or pressure). Effect direction for height follows what I would expect, but not pressure (hope I’m recalling science correctly here).

Dependent variable:
pm25
(1) (2) (3)
aot_anom 4.765*** 5.167*** 5.430***
(1.040) (1.512) (1.515)
aot_anom:smoke_day 46.930*** 52.769*** 63.912***
(1.054) (1.588) (2.427)
aot_anom:smoke_day:height -0.004***
(0.0003)
aot_anom:smoke_day:pressure -0.024***
(0.002)
Constant 7.579*** 8.197*** 8.048***
(0.071) (0.120) (0.121)
Observations 92,536 52,928 52,928
R2 0.415 0.400 0.398
Adjusted R2 0.415 0.400 0.397
Residual Std. Error 20.818 (df = 92533) 25.792 (df = 52924) 25.839 (df = 52924)
F Statistic 32,802.810*** (df = 2; 92533) 11,745.030*** (df = 3; 52924) 11,639.840*** (df = 3; 52924)
Note: p<0.1; p<0.05; p<0.01

However, I am not sure if this is the right regression or if we should still include fixed effects of some sort. If I’ve understood correctly, anomalizing AOT accomplishes what FEs had originally been intended to do, which was capture background AOT. But I guess we may also want to capture background PM2.5? So we’ll try that later in the document.

Before that, let’s get predicted PM2.5 and compare the time series from August to Mid-October in the Bay Area.

df$pred_aotanom_smokeday <- predict(mdl0)
df[!is.na(df$height), "pred_aotanom_smokeday_height"] <- predict(mdl1)
df[!is.na(df$pressure), "pred_aotanom_smokeday_pressure"] <- predict(mdl2)
t1 <- as.Date("2020-08-01")
t2 <- as.Date("2020-10-15")
event <- df %>% 
  filter(t1 <= date, date <= t2, cbsa_name == "San Francisco-Oakland-Hayward, CA") %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

It look like all the models are capturing the big spike and then overestimating the medium spikes sometimes. Not much difference between the model predictions, except we’re missing predictions where height and pressure are missing due to no match between EPA station and trajectory points.

Now we use demeaned PM2.5

We regress PM2.5 on fixed effects for EPA station ID and month of the year (we only have 2020 here) to get demeaned PM2.5 values.

df <- dat_merged1

# Get residuals and fitted values
mdlfe <- feols(pm25 ~ 1 | id_epa + month, data = df)
df$pm25_dmn <- mdlfe$resid
df$fv <- mdlfe$fitted.values

Same models as before but now on demeaned PM2.5.

mdl0 <- lm(pm25_dmn ~ aot_anom + aot_anom:smoke_day,
           data = df)
mdl1 <- lm(pm25_dmn ~ aot_anom + aot_anom:smoke_day + aot_anom:smoke_day:height,
           data = df)
mdl2 <- lm(pm25_dmn ~ aot_anom + aot_anom:smoke_day + aot_anom:smoke_day:pressure,
           data = df)

These R2 values now look more like the ones from the last notebook I saw. R2 is slightly larger for models with height or pressure, and model with height does best. All terms are significant.

Dependent variable:
pm25_dmn
(1) (2) (3)
aot_anom -7.600*** -6.565*** -6.346***
(1.069) (1.506) (1.513)
aot_anom:smoke_day 44.175*** 52.799*** 47.864***
(1.084) (1.582) (2.424)
aot_anom:smoke_day:height -0.006***
(0.0003)
aot_anom:smoke_day:pressure -0.006***
(0.002)
Constant -3.423*** -4.226*** -4.350***
(0.073) (0.120) (0.121)
Observations 92,536 52,928 52,928
R2 0.252 0.264 0.257
Adjusted R2 0.252 0.264 0.257
Residual Std. Error 21.413 (df = 92533) 25.696 (df = 52924) 25.815 (df = 52924)
F Statistic 15,589.270*** (df = 2; 92533) 6,327.997*** (df = 3; 52924) 6,108.691*** (df = 3; 52924)
Note: p<0.1; p<0.05; p<0.01

We add back in the fitted values from the FE regression, get predicted PM2.5 (fitted value from FE reg + residual predicted by our models), and compare the time series from August to Mid-October in the Bay Area.

df$pred_dmn_aotanom_smokeday <- predict(mdl0)
df[!is.na(df$height), "pred_dmn_aotanom_smokeday_height"] <- predict(mdl1)
df[!is.na(df$pressure), "pred_dmn_aotanom_smokeday_pressure"] <- predict(mdl2)

df <- df %>% 
  mutate(across(starts_with("pred_"), as.numeric),
         pred_dmn_aotanom_smokeday = pred_dmn_aotanom_smokeday + fv,
         pred_dmn_aotanom_smokeday_height = pred_dmn_aotanom_smokeday_height + fv,
         pred_dmn_aotanom_smokeday_pressure = pred_dmn_aotanom_smokeday_pressure + fv)
event <- df %>% 
  filter(t1 <= date, date <= t2, cbsa_name == "San Francisco-Oakland-Hayward, CA") %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Models capture the big spike, though now slightly underestimating. But now greatly overestimating the big dip in later September. Overestimating the rise in early September. Overestimating the lower PM2.5 values generally. Still have weird inversions in later August, though not as severe as in the previous graph.

It looks like models with just anomalous AOT and smoke day are able to capture the big spike, so I wonder if height is really needed, especially considering the missingness due to matching, or if I’ve done something wrong somewhere…

Update 1

We change to using the 100 km cutoff so that we don’t miss observations across models.

# Re-configure merged data to use max cutoff (100 km)
agg <- "idw"
cut <- 100
dat_merged1 <- dat_merged %>% filter(agg_method == agg, cutoff == cut)

We change our baseline model to
* \(PM_{2.5} \sim AOT_{anom} + smokeday + AOT_{anom} \times smokeday + stationFE + samplemonthFE\)
and compare with
* \(PM_{2.5} \sim AOT_{anom} + smokeday + AOT_{anom} \times smokeday + AOT_{anom} \times smokeday \times height + stationFE + samplemonthFE\)
* \(PM_{2.5} \sim AOT_{anom} + smokeday + AOT_{anom} \times smokeday + AOT_{anom} \times smokeday \times pressure + stationFE + samplemonthFE\)

df <- dat_merged1
mdl0 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day | id_epa + month,
              data = df)
mdl1 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:height | id_epa + month,
              data = df)
mdl2 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:pressure | id_epa + month,
              data = df)

Within R2 is around 0.34 and is highest for model with height, though ultimately not differing much across models. Smoke day term and pressure interaction term are not significant. Interaction term with height is significant, and coefficient goes in expected direction but is small.

etable(mdl0, mdl1, mdl2, drop.section = "fixef")

Let’s get predicted PM2.5 and plot some comparisons.

df$pred_aotanom_smokeday <- predict(mdl0)
df$pred_aotanom_smokeday_height <- predict(mdl1)
df$pred_aotanom_smokeday_pressure <- predict(mdl2)

We filter to smoke days and compare the predictions of each model to observed PM2.5 values. They look very much the same. Seems there’s still quite some unexplained variation in PM2.5.

dfsd <- df %>% 
  filter(smoke_day == 1) %>% 
  pivot_longer(starts_with("pred_"), names_to = "mdl")
ggplot(data = dfsd, mapping = aes(x = value, y = pm25)) + 
  geom_point(aes(color = mdl), alpha = 0.2) + 
  facet_wrap(vars(mdl))

Here’s a correlation plot to confirm that the predictions are very close across the models.

dfsd <- df %>% 
  filter(smoke_day == 1) %>% 
  select(pm25, starts_with("pred_"))
corr_dfsd <- cor(dfsd, use = "complete.obs")
ggcorrplot(corr_dfsd, lab = TRUE)

Let’s try plotting time series where baseline model underestimates.

Looks like Mono county PM2.5 gets underestimated the most.

df$resid0 <- mdl0$residuals
df %>% slice_max(resid0, n = 50) %>% count(county) %>% arrange(desc(n))

The Slink Fire in Mono county was reported on August 29th and last updated October 22nd. So we plot the time series from mid-August through the end of October.

t1 <- as.Date("2020-08-15")
t2 <- as.Date("2020-10-31")
event <- df %>% 
  filter(t1 <= date, date <= t2, county == "Mono") %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

In addition to the baseline model underestimating PM2.5, our models with height or pressure don’t quite capture most spikes, nor do they differ much from the baseline model.

Let’s also take a look at our runner-up, Lane county.
The Holiday Farm Fire was reported on September 7th and contained on October 29th. So we plot the time series from late August to mid-November.

t1 <- as.Date("2020-08-22")
t2 <- as.Date("2020-11-15")
event <- df %>% 
  filter(t1 <= date, date <= t2, county == "Lane") %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Our models underestimate PM2.5, but it’s not as bad as above. Also overestimates PM2.5 levels near zero. Model with height captures spike just before hitting the top of the big spike slightly better than the others.

Let’s take a look at a big fire and see how our models perform.
The August Complex started August 17th and was contained on November 15th. It burned in Glenn, Lake, Mendocino, Tehama, Trinity, and Shasta counties. So we plot the time series from early August to the end of November.

t1 <- as.Date("2020-08-01")
t2 <- as.Date("2020-11-30")
event <- df %>% 
  filter(t1 <= date, date <= t2, 
         county %in% c("Glenn", "Lake", "Mendocino", "Tehama", "Trinity", "Shasta")) %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Our models capture the big spike but noticeably overestimate the spike in late August. They also underestimate early August and overestimate a bit in early September. The model predictions overlap a lot, but at a few spikes the model with height seems to estimate a bit higher than the others.

Perhaps there are better ways of analyzing this, but based on R2 and plots, it seems to me that height isn’t making a big difference from our baseline model.

Update 2

We change to 30 km cutoff and fit our baseline model and the model with height using the same sample.

# Re-configure merged data to use 30 km cutoff
agg <- "idw"
cut <- 30
dat_merged1 <- dat_merged %>% filter(agg_method == agg, cutoff == cut)

# Drop obs where missing height
dat_merged1 <- dat_merged1 %>% drop_na(height)
df <- dat_merged1
mdl0 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day | id_epa + month,
              data = df)
mdl1 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:height | id_epa + month,
              data = df)
mdl2 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:pressure | id_epa + month,
              data = df)

Compared to using the 100 km cutoff, within R2 is a bit lower here. The model with height does best, though R2 differences are small. Coefficient on term with height seems similar to above, i.e. right direction but small. Coefficient on term with height is smaller in magnitude now. Also just noticed that coefficient on just smoke day term is negative, not sure if that makes sense.

etable(mdl0, mdl1, mdl2, drop.section = "fixef")

Let’s compare the models’ predicted PM2.5.

df$pred_aotanom_smokeday <- predict(mdl0)
df$pred_aotanom_smokeday_height <- predict(mdl1)
df$pred_aotanom_smokeday_pressure <- predict(mdl2)

We filter to smoke days and compare the predictions using their correlations.

dfsd <- df %>% 
  filter(smoke_day == 1) %>% 
  select(pm25, starts_with("pred_"))
corr_dfsd <- cor(dfsd, use = "complete.obs")
ggcorrplot(corr_dfsd, lab = TRUE)

Doesn’t seem like 30 km cutoff helped height have larger effect.

I wonder how much the choice of initial height parameters influences our results.

Adding here time series plot of August Complex with only observed PM2.5 and baseline model to help see lines more clearly.

t1 <- as.Date("2020-08-01")
t2 <- as.Date("2020-11-30")
event <- df %>% 
  select(-pred_aotanom_smokeday_height, -pred_aotanom_smokeday_pressure) %>% 
  filter(t1 <= date, date <= t2, 
         county %in% c("Glenn", "Lake", "Mendocino", "Tehama", "Trinity", "Shasta")) %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Update 3

Limit to California

We limit to EPA stations in California since trajectories were initiated in California and may not represent plumes in other states.

# Drop obs not in California
loc_epa <- dat_merged[c("lon_epa", "lat_epa")] %>% distinct()
us_states <- states() %>% as("Spatial")
us_states <- SpatialPolygonsDataFrame(SpatialPolygons(us_states@polygons), 
                                      us_states@data %>% select(NAME))
o <- over(SpatialPoints(loc_epa), us_states)
dat_merged0 <- dat_merged %>% 
  left_join(bind_cols(loc_epa, o)) %>% 
  rename(state = NAME) %>% 
  filter(state == "California")
agg <- "idw"
cut <- 30
dat_merged1 <- dat_merged0 %>% filter(agg_method == agg, cutoff == cut)
dat_merged1 <- dat_merged1 %>% drop_na(height)
df <- dat_merged1
mdl0 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day | id_epa + month,
              data = df)
mdl1 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:height | id_epa + month,
              data =df)
mdl2 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:pressure | id_epa + month,
              data = df)

Lost a little over half our obs to filtering for California. Smoke day alone coefficient is positive now. Compared to above, coefficient on anomalous AOT is bigger and coefficient on anomalous AOT and smoke day interaction term is smaller for baseline and height models and larger for pressure model. Interaction term with height is no longer statistically significant. Within R2 is lower now. Model with height still performs better than baseline model, but R2 differences are small, and now model with pressure performs best.

etable(mdl0, mdl1, mdl2, drop.section = "fixef")

We get the models’ predicted PM2.5. We stick to total PM2.5 for now. Plots of specifically smoke PM2.5 will come in next section.

df$pred_aotanom_smokeday <- predict(mdl0)
df$pred_aotanom_smokeday_height <- predict(mdl1)
df$pred_aotanom_smokeday_pressure <- predict(mdl2)

Predictions still very similar, though model with pressure more different than model with height when compared against baseline.

dfsd <- df %>% 
  filter(smoke_day == 1) %>% 
  select(pm25, starts_with("pred_"))
corr_dfsd <- cor(dfsd, use = "complete.obs")
ggcorrplot(corr_dfsd, lab = TRUE)

Plotting time series for Bay Area anew.

event <- df %>% 
  filter(t1 <= date, date <= t2, cbsa_name == "San Francisco-Oakland-Hayward, CA") %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Models underestimate the big peak, but model with height and baseline get a bit more of the top spike than does model with pressure. Medium peaks somewhat captured. Lower levels of PM2.5 get overestimated a bit.

Mono county gets underestimated the most again.

df$resid0 <- mdl0$residuals
df %>% slice_max(resid0, n = 50) %>% count(county) %>% arrange(desc(n))

Slink Fire again.

t1 <- as.Date("2020-08-15")
t2 <- as.Date("2020-10-31")
event <- df %>% 
  filter(t1 <= date, date <= t2, county == "Mono") %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Model with pressure captures peaks a bit better. All models overestimate low levels of PM2.5.

August Complex again.

t1 <- as.Date("2020-08-01")
t2 <- as.Date("2020-11-30")
event <- df %>% 
  filter(t1 <= date, date <= t2, 
         county %in% c("Glenn", "Lake", "Mendocino", "Tehama", "Trinity", "Shasta")) %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Model with pressure captures big peak best. All models overestimate late August peak and underestimate late September peaks.

IDW average within-trajectory minimum height

Now we use IDW average of daily within-trajectory minimum heights within buffer zone. In other words, for each EPA station-day-PM2.5 combination, first found trajectory points on the same day within a set radius, next grouped by trajectory ID and found trajectory point with minimum height (maximum pressure) for each trajectory, and then IDW average the height (pressure) values across trajectories.

# New option "extr" for using within-trajectory extrema
agg <- "extr"
cut <- 30
dat_merged1 <- dat_merged0 %>% filter(agg_method == agg, cutoff == cut)
dat_merged1 <- dat_merged1 %>% drop_na(height)
df <- dat_merged1
mdl0 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day | id_epa + month,
              data = df)
mdl1 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:height | id_epa + month,
              data = df)
mdl2 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:pressure | id_epa + month,
              data  = df)

Results are pretty similar to above. So the change in matching method doesn’t seem to make a big difference.

etable(mdl0, mdl1, mdl2, drop.section = "fixef")

Now we predict and plot smoke PM2.5. Not sure if FEs are also supposed to go in here so just not including for now.

smoke_day <- df$smoke_day
aot_anom <- df$aot_anom
height <- df$height
pressure <- df$pressure

coefs <- coefficients(mdl0)
df$pred_aotanom_smokeday <- coefs["smoke_day"]*smoke_day + 
  coefs["aot_anom:smoke_day"]*aot_anom*smoke_day
coefs <- coefficients(mdl1)
df$pred_aotanom_smokeday_height <- coefs["smoke_day"]*smoke_day + 
  coefs["aot_anom:smoke_day"]*aot_anom*smoke_day + 
  coefs["aot_anom:smoke_day:height"]*aot_anom*smoke_day*height
coefs <- coefficients(mdl2)
df$pred_aotanom_smokeday_pressure <- coefs["smoke_day"]*smoke_day + 
  coefs["aot_anom:smoke_day"]*aot_anom*smoke_day + 
  coefs["aot_anom:smoke_day:pressure"]*aot_anom*smoke_day*pressure

We filter to smoke days and compare the predictions of each model to total PM2.5 values. Pressure model seems to do slightly better, but predictions don’t get past 150 micrograms, so seems we probably won’t be able to capture big spikes.

dfsd <- df %>% 
  filter(smoke_day == 1) %>% 
  pivot_longer(starts_with("pred_"), names_to = "mdl")
ggplot(data = dfsd, mapping = aes(x = value, y = pm25)) + 
  geom_point(aes(color = mdl), alpha = 0.2) + 
  facet_wrap(vars(mdl))

Bay Area again. Since we’re predicting smoke PM2.5 and not total PM2.5, predictions are lower now. Pressure model does worse than baseline and height models in late September/early October, but otherwise the models are pretty similar.

event <- df %>% 
  filter(t1 <= date, date <= t2, cbsa_name == "San Francisco-Oakland-Hayward, CA") %>% 
  group_by(date) %>% 
  summarize(across(c(pm25, starts_with("pred")), mean, na.rm = TRUE)) %>% 
  pivot_longer(c(pm25, starts_with("pred_")), names_to = "mdl")

ggplot(data = event, mapping = aes(x = date, y = value)) +
  geom_line(mapping = aes(color = mdl)) +
  theme_classic()

Pressure model predictions differ from baseline model predictions by at most about 61 micrograms, though this is uncommon.

diff_height <- df$pred_aotanom_smokeday_height - df$pred_aotanom_smokeday
summary(diff_height)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-16.45710   0.00000   0.00000  -0.01706   0.00000  10.47510 
quantile(diff_height, c(0.01, 0.05, 0.95, 0.99))
        1%         5%        95%        99% 
-2.9539452 -0.7675867  0.6609877  2.7859131 
diff_pressure <- df$pred_aotanom_smokeday_pressure - df$pred_aotanom_smokeday
summary(diff_pressure)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-52.71067   0.00000   0.00000   0.05918   0.00000  61.10585 
quantile(diff_pressure, c(0.01, 0.05, 0.95, 0.99))
       1%        5%       95%       99% 
-9.887987 -2.609854  2.780110 11.325619 

From the above, height doesn’t generally seem to help much.

Also took a look at Brey et al. again and a few questions popped up since we follow them up to initializing trajectories but not after that:
- Should we still try to match trajectory points to plumes? Brey et al. throws away trajectories that don’t overlap a plume in the first 48 hours. Currently, we don’t do any overlapping with plume, but if there’s a lot of false positives, it could help to overlap trajectories with plumes first.
- Brey et al. throws away trajectory points once they reach 0 meters in height. I’m not sure why they do this, but is this something we should consider doing? It is possible for particles to sit on the ground for a while and then pick back up into the atmosphere (very briefly looked into this in the data before out of curiosity).

Update 4

EPA sometimes records multiple PM2.5 values for the same station-day, so let’s try averaging each station-day’s PM2.5 values and see if that changes anything.

dat_merged1 <- dat_merged0 %>% 
  filter(agg_method == agg, cutoff == cut) %>% 
  drop_na(height)
dat_merged1 <- dat_merged0 %>% 
  filter(agg_method == agg, cutoff == cut) %>% 
  drop_na(height)
dfpm <- dat_merged1 %>% 
  group_by(date, id_epa) %>% 
  summarize(pm25 = mean(pm25)) %>% 
  ungroup()
`summarise()` has grouped output by 'date'. You can override using the `.groups` argument.
dat_merged1 <- dat_merged0 %>% 
  filter(agg_method == agg, cutoff == cut) %>% 
  drop_na(height)
dfpm <- dat_merged1 %>% 
  group_by(date, id_epa) %>% 
  summarize(pm25 = mean(pm25)) %>% 
  ungroup()
`summarise()` has grouped output by 'date'. You can override using the `.groups` argument.
dat_merged1 <- dat_merged1 %>% 
  select(-pm25) %>% 
  distinct() %>% 
  left_join(dfpm)
Joining, by = c("date", "id_epa")
df <- dat_merged1
mdl0 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day | id_epa + month,
              data = df)
mdl1 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:height | id_epa + month,
              data = df)
mdl2 <- feols(pm25 ~ aot_anom + smoke_day + aot_anom:smoke_day + aot_anom:smoke_day:pressure | id_epa + month,
              data = df)
etable(mdl0, mdl1, mdl2, drop.section = "fixef")

Doesn’t look much different from above.

LS0tCnRpdGxlOiAiRG9lcyBQbHVtZSBIZWlnaHQgZnJvbSBIWVNQTElUIEltcHJvdmUgRGFpbHktTGV2ZWwgUE0yLjUgUHJlZGljdGlvbj8iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShmaXhlc3QpCmxpYnJhcnkoc3RhcmdhemVyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2djb3JycGxvdCkKbGlicmFyeShzcCkKbGlicmFyeSh0aWdyaXMpCgojIFJlYWQgaW4gbWVyZ2VkIGRhdGEKcGF0aF9kcm9wYm94IDwtICIvVXNlcnMvamVzc3NzbGkvQnVya2VMYWIgRHJvcGJveC9EYXRhLyIKZGF0X21lcmdlZCA8LSByZWFkUkRTKHBhc3RlMChwYXRoX2Ryb3Bib3gsICJobXNfaHlzcGxpdC90cmFqZWN0b3JpZXMvdHJhamVjdG9yaWVzX0VQQV9zbW9rZV9BT1RfQ0FfMjAyMC5yZHMiKSkgJT4lIAogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlKSwgCiAgICAgICAgIHllYXIgPSB5ZWFyKGRhdGUpLAogICAgICAgICBtb250aCA9IG1vbnRoKGRhdGUpLAogICAgICAgICBkYXkgPSBkYXkoZGF0ZSkpCmBgYAoKU3RhcnQgd2l0aCBkYXRhIGNvbnRhaW5pbmcgUE0yLjUsIGFub21hbG91cyBBT1QsIHNtb2tlIGRheSwgaGVpZ2h0IGluIG1ldGVycyAKYWJvdmUgZ3JvdW5kIGxldmVsLCBhbmQgcHJlc3N1cmUgaW4gaFBhLiBEYXRhIGlzIHRoZSByZXN1bHQgb2YgbWF0Y2hpbmcgNzItaG91ciAKdHJhamVjdG9yaWVzIGluaXRpYWxpemVkIGZyb20gSFlTUExJVCBwb2ludHMgaW4gQ2FsaWZvcm5pYSAyMDIwIGZvbGxvd2luZwpCcmV5IGV0IGFsLiB3aXRoIEVQQSBzdGF0aW9uLWRheS1QTTIuNSBkYXRhIGFuZCB0aGVuIG1lcmdpbmcgd2l0aCAxMCBrbSBncmlkIG9mCkFPVCwgbWVhbiBBT1QsIGFuZCBzbW9rZSBkYXkuIEFub21hbG91cyBBT1QgaXMgQU9UIC0gbWVhbiBBT1QuCgpBZ2dyZWdhdGlvbiBtZXRob2QgYW5kIGN1dG9mZiBkZXRlcm1pbmUgaG93IHRyYWplY3RvcnkgcG9pbnRzIHdlcmUgbWF0Y2hlZCB0bwpFUEEgc3RhdGlvbnMuCgpgYGB7cn0KIyBDaG9vc2UgYWdncmVnYXRpb24gbWV0aG9kOiBubiwgaWR3LCBvciBrIChpZHcgb2Yga25uKQphZ2cgPC0gImlkdyIKCiMgQ2hvb3NlIGN1dG9mZjogMTAsIDMwLCA1MCwgODAsIG9yIDEwMCBrbQpjdXQgPC0gMzAKCiMgQ29uZmlndXJlIGRhdGEgZnJhbWUgYmFzZWQgb24gdGhlIGFib3ZlCmRhdF9tZXJnZWQxIDwtIGRhdF9tZXJnZWQgJT4lIGZpbHRlcihhZ2dfbWV0aG9kID09IGFnZywgY3V0b2ZmID09IGN1dCkKYGBgCgojIyBMZXQncyBzdGFydCBtb2RlbGluZwoKV2UgbW9kZWwgUE0yLjUgIAogICogJFBNX3syLjV9IFxzaW0gQU9UX3thbm9tfSArIEFPVF97YW5vbX0gXHRpbWVzIHNtb2tlZGF5JCAgCiAgKiAkUE1fezIuNX0gXHNpbSBBT1Rfe2Fub219ICsgQU9UX3thbm9tfSBcdGltZXMgc21va2VkYXkgKyBBT1Rfe2Fub219IFx0aW1lcyBzbW9rZWRheSBcdGltZXMgaGVpZ2h0JCAgCiAgKiAkUE1fezIuNX0gXHNpbSBBT1Rfe2Fub219ICsgQU9UX3thbm9tfSBcdGltZXMgc21va2VkYXkgKyBBT1Rfe2Fub219IFx0aW1lcyBzbW9rZWRheSBcdGltZXMgcHJlc3N1cmUkICAKCmBgYHtyfQpkZiA8LSBkYXRfbWVyZ2VkMQptZGwwIDwtIGxtKHBtMjUgfiBhb3RfYW5vbSArIGFvdF9hbm9tOnNtb2tlX2RheSwKICAgICAgICAgICBkYXRhID0gZGYpCm1kbDEgPC0gbG0ocG0yNSB+IGFvdF9hbm9tICsgYW90X2Fub206c21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5OmhlaWdodCwKICAgICAgICAgICBkYXRhID0gZGYpCm1kbDIgPC0gbG0ocG0yNSB+IGFvdF9hbm9tICsgYW90X2Fub206c21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5OnByZXNzdXJlLAogICAgICAgICAgIGRhdGEgPSBkZikKYGBgCgpNb2RlbCB3aXRoIGp1c3QgYW5vbWFsb3VzIEFPVCBhbmQgc21va2UgZGF5IGRvZXMgYmV0dGVyIHRoYW4gbW9kZWxzIHdpdGggaGVpZ2h0Cm9yIHByZXNzdXJlLiBCYXNlZCBvbiB0aGUgbGFzdCBQTTIuNSBtb2RlbGluZyBub3RlYm9vayBJIHNhdywgSSB0aGluayB0aGVzZQpSMiB2YWx1ZXMgYXJlIGhpZ2hlcj8gQWxsIHRlcm1zIGFyZSBzaWduaWZpY2FudCwgYnV0IGVmZmVjdCBzaXplcyBhcmUgc21hbGwgZm9yCnRlcm1zIHdpdGggaGVpZ2h0IG9yIHByZXNzdXJlIChvciBtYXliZSB0aGV5J3JlIG5vdCBzbWFsbCBhbmQgaXQncyBqdXN0IGFuIAphcnRpZmFjdCBvZiB1bml0cyBtZWFzdXJpbmcgaGVpZ2h0IG9yIHByZXNzdXJlKS4gRWZmZWN0IGRpcmVjdGlvbiBmb3IgaGVpZ2h0IApmb2xsb3dzIHdoYXQgSSB3b3VsZCBleHBlY3QsIGJ1dCBub3QgcHJlc3N1cmUgKGhvcGUgSSdtIHJlY2FsbGluZyBzY2llbmNlIApjb3JyZWN0bHkgaGVyZSkuCgpgYGB7ciBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnYXNpcyd9CnN1cHByZXNzV2FybmluZ3Moc3RhcmdhemVyKG1kbDAsIG1kbDEsIG1kbDIsIHR5cGUgPSAiaHRtbCIpKQpgYGAKCgpIb3dldmVyLCBJIGFtIG5vdCBzdXJlIGlmIHRoaXMgaXMgdGhlIHJpZ2h0IHJlZ3Jlc3Npb24gb3IgaWYgd2UKc2hvdWxkIHN0aWxsIGluY2x1ZGUgZml4ZWQgZWZmZWN0cyBvZiBzb21lIHNvcnQuIElmIEkndmUgdW5kZXJzdG9vZCBjb3JyZWN0bHksCmFub21hbGl6aW5nIEFPVCBhY2NvbXBsaXNoZXMgd2hhdCBGRXMgaGFkIG9yaWdpbmFsbHkgYmVlbiBpbnRlbmRlZCB0byBkbywgd2hpY2gKd2FzIGNhcHR1cmUgYmFja2dyb3VuZCBBT1QuIEJ1dCBJIGd1ZXNzIHdlIG1heSBhbHNvIHdhbnQgdG8gY2FwdHVyZSBiYWNrZ3JvdW5kIApQTTIuNT8gU28gd2UnbGwgdHJ5IHRoYXQgbGF0ZXIgaW4gdGhlIGRvY3VtZW50LgoKQmVmb3JlIHRoYXQsIGxldCdzIGdldCBwcmVkaWN0ZWQgUE0yLjUgYW5kIGNvbXBhcmUgdGhlIHRpbWUgc2VyaWVzIGZyb20gQXVndXN0IAp0byBNaWQtT2N0b2JlciBpbiB0aGUgQmF5IEFyZWEuCgpgYGB7cn0KZGYkcHJlZF9hb3Rhbm9tX3Ntb2tlZGF5IDwtIHByZWRpY3QobWRsMCkKZGZbIWlzLm5hKGRmJGhlaWdodCksICJwcmVkX2FvdGFub21fc21va2VkYXlfaGVpZ2h0Il0gPC0gcHJlZGljdChtZGwxKQpkZlshaXMubmEoZGYkcHJlc3N1cmUpLCAicHJlZF9hb3Rhbm9tX3Ntb2tlZGF5X3ByZXNzdXJlIl0gPC0gcHJlZGljdChtZGwyKQpgYGAKCmBgYHtyfQp0MSA8LSBhcy5EYXRlKCIyMDIwLTA4LTAxIikKdDIgPC0gYXMuRGF0ZSgiMjAyMC0xMC0xNSIpCmV2ZW50IDwtIGRmICU+JSAKICBmaWx0ZXIodDEgPD0gZGF0ZSwgZGF0ZSA8PSB0MiwgY2JzYV9uYW1lID09ICJTYW4gRnJhbmNpc2NvLU9ha2xhbmQtSGF5d2FyZCwgQ0EiKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYyhwbTI1LCBzdGFydHNfd2l0aCgicHJlZCIpKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lIAogIHBpdm90X2xvbmdlcihjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkXyIpKSwgbmFtZXNfdG8gPSAibWRsIikKCmdncGxvdChkYXRhID0gZXZlbnQsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvciA9IG1kbCkpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpJdCBsb29rIGxpa2UgYWxsIHRoZSBtb2RlbHMgYXJlIGNhcHR1cmluZyB0aGUgYmlnIHNwaWtlIGFuZCB0aGVuIG92ZXJlc3RpbWF0aW5nIAp0aGUgbWVkaXVtIHNwaWtlcyBzb21ldGltZXMuIE5vdCBtdWNoIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbW9kZWwgcHJlZGljdGlvbnMsIApleGNlcHQgd2UncmUgbWlzc2luZyBwcmVkaWN0aW9ucyB3aGVyZSBoZWlnaHQgYW5kIHByZXNzdXJlIGFyZSBtaXNzaW5nIGR1ZSB0byBubyAKbWF0Y2ggYmV0d2VlbiBFUEEgc3RhdGlvbiBhbmQgdHJhamVjdG9yeSBwb2ludHMuCgojIyBOb3cgd2UgdXNlIGRlbWVhbmVkIFBNMi41CgpXZSByZWdyZXNzIFBNMi41IG9uIGZpeGVkIGVmZmVjdHMgZm9yIEVQQSBzdGF0aW9uIElEIGFuZCBtb250aCBvZiB0aGUgeWVhciAod2UgCm9ubHkgaGF2ZSAyMDIwIGhlcmUpIHRvIGdldCBkZW1lYW5lZCBQTTIuNSB2YWx1ZXMuCgpgYGB7cn0KZGYgPC0gZGF0X21lcmdlZDEKCiMgR2V0IHJlc2lkdWFscyBhbmQgZml0dGVkIHZhbHVlcwptZGxmZSA8LSBmZW9scyhwbTI1IH4gMSB8IGlkX2VwYSArIG1vbnRoLCBkYXRhID0gZGYpCmRmJHBtMjVfZG1uIDwtIG1kbGZlJHJlc2lkCmRmJGZ2IDwtIG1kbGZlJGZpdHRlZC52YWx1ZXMKYGBgCgpTYW1lIG1vZGVscyBhcyBiZWZvcmUgYnV0IG5vdyBvbiBkZW1lYW5lZCBQTTIuNS4KCmBgYHtyfQptZGwwIDwtIGxtKHBtMjVfZG1uIH4gYW90X2Fub20gKyBhb3RfYW5vbTpzbW9rZV9kYXksCiAgICAgICAgICAgZGF0YSA9IGRmKQptZGwxIDwtIGxtKHBtMjVfZG1uIH4gYW90X2Fub20gKyBhb3RfYW5vbTpzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXk6aGVpZ2h0LAogICAgICAgICAgIGRhdGEgPSBkZikKbWRsMiA8LSBsbShwbTI1X2RtbiB+IGFvdF9hbm9tICsgYW90X2Fub206c21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5OnByZXNzdXJlLAogICAgICAgICAgIGRhdGEgPSBkZikKYGBgCgpUaGVzZSBSMiB2YWx1ZXMgbm93IGxvb2sgbW9yZSBsaWtlIHRoZSBvbmVzIGZyb20gdGhlIGxhc3Qgbm90ZWJvb2sgSSBzYXcuIFIyIGlzIApzbGlnaHRseSBsYXJnZXIgZm9yIG1vZGVscyB3aXRoIGhlaWdodCBvciBwcmVzc3VyZSwgYW5kIG1vZGVsIHdpdGggaGVpZ2h0IGRvZXMgCmJlc3QuIEFsbCB0ZXJtcyBhcmUgc2lnbmlmaWNhbnQuCgpgYGB7ciBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnYXNpcyd9CnN1cHByZXNzV2FybmluZ3Moc3RhcmdhemVyKG1kbDAsIG1kbDEsIG1kbDIsIHR5cGUgPSAiaHRtbCIpKQpgYGAKCldlIGFkZCBiYWNrIGluIHRoZSBmaXR0ZWQgdmFsdWVzIGZyb20gdGhlIEZFIHJlZ3Jlc3Npb24sIGdldCBwcmVkaWN0ZWQgUE0yLjUgCihmaXR0ZWQgdmFsdWUgZnJvbSBGRSByZWcgKyByZXNpZHVhbCBwcmVkaWN0ZWQgYnkgb3VyIG1vZGVscyksIGFuZCBjb21wYXJlIHRoZSAKdGltZSBzZXJpZXMgZnJvbSBBdWd1c3QgdG8gTWlkLU9jdG9iZXIgaW4gdGhlIEJheSBBcmVhLgoKYGBge3J9CmRmJHByZWRfZG1uX2FvdGFub21fc21va2VkYXkgPC0gcHJlZGljdChtZGwwKQpkZlshaXMubmEoZGYkaGVpZ2h0KSwgInByZWRfZG1uX2FvdGFub21fc21va2VkYXlfaGVpZ2h0Il0gPC0gcHJlZGljdChtZGwxKQpkZlshaXMubmEoZGYkcHJlc3N1cmUpLCAicHJlZF9kbW5fYW90YW5vbV9zbW9rZWRheV9wcmVzc3VyZSJdIDwtIHByZWRpY3QobWRsMikKCmRmIDwtIGRmICU+JSAKICBtdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCJwcmVkXyIpLCBhcy5udW1lcmljKSwKICAgICAgICAgcHJlZF9kbW5fYW90YW5vbV9zbW9rZWRheSA9IHByZWRfZG1uX2FvdGFub21fc21va2VkYXkgKyBmdiwKICAgICAgICAgcHJlZF9kbW5fYW90YW5vbV9zbW9rZWRheV9oZWlnaHQgPSBwcmVkX2Rtbl9hb3Rhbm9tX3Ntb2tlZGF5X2hlaWdodCArIGZ2LAogICAgICAgICBwcmVkX2Rtbl9hb3Rhbm9tX3Ntb2tlZGF5X3ByZXNzdXJlID0gcHJlZF9kbW5fYW90YW5vbV9zbW9rZWRheV9wcmVzc3VyZSArIGZ2KQpgYGAKCmBgYHtyfQpldmVudCA8LSBkZiAlPiUgCiAgZmlsdGVyKHQxIDw9IGRhdGUsIGRhdGUgPD0gdDIsIGNic2FfbmFtZSA9PSAiU2FuIEZyYW5jaXNjby1PYWtsYW5kLUhheXdhcmQsIENBIikgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JSAKICBzdW1tYXJpemUoYWNyb3NzKGMocG0yNSwgc3RhcnRzX3dpdGgoInByZWQiKSksIG1lYW4sIG5hLnJtID0gVFJVRSkpICU+JSAKICBwaXZvdF9sb25nZXIoYyhwbTI1LCBzdGFydHNfd2l0aCgicHJlZF8iKSksIG5hbWVzX3RvID0gIm1kbCIpCgpnZ3Bsb3QoZGF0YSA9IGV2ZW50LCBtYXBwaW5nID0gYWVzKHggPSBkYXRlLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoY29sb3IgPSBtZGwpKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKTW9kZWxzIGNhcHR1cmUgdGhlIGJpZyBzcGlrZSwgdGhvdWdoIG5vdyBzbGlnaHRseSB1bmRlcmVzdGltYXRpbmcuIEJ1dCBub3cgCmdyZWF0bHkgb3ZlcmVzdGltYXRpbmcgdGhlIGJpZyBkaXAgaW4gbGF0ZXIgU2VwdGVtYmVyLiBPdmVyZXN0aW1hdGluZyB0aGUgcmlzZSAKaW4gZWFybHkgU2VwdGVtYmVyLiBPdmVyZXN0aW1hdGluZyB0aGUgbG93ZXIgUE0yLjUgdmFsdWVzIGdlbmVyYWxseS4gU3RpbGwgaGF2ZSAKd2VpcmQgaW52ZXJzaW9ucyBpbiBsYXRlciBBdWd1c3QsIHRob3VnaCBub3QgYXMgc2V2ZXJlIGFzIGluIHRoZSBwcmV2aW91cyBncmFwaC4KCkl0IGxvb2tzIGxpa2UgbW9kZWxzIHdpdGgganVzdCBhbm9tYWxvdXMgQU9UIGFuZCBzbW9rZSBkYXkgYXJlIGFibGUgdG8KY2FwdHVyZSB0aGUgYmlnIHNwaWtlLCBzbyBJIHdvbmRlciBpZiBoZWlnaHQgaXMgcmVhbGx5IG5lZWRlZCwgZXNwZWNpYWxseSAKY29uc2lkZXJpbmcgdGhlIG1pc3NpbmduZXNzIGR1ZSB0byBtYXRjaGluZywgb3IgaWYgSSd2ZSBkb25lIHNvbWV0aGluZyB3cm9uZyAKc29tZXdoZXJlLi4uCgojIyBVcGRhdGUgMQoKV2UgY2hhbmdlIHRvIHVzaW5nIHRoZSAxMDAga20gY3V0b2ZmIHNvIHRoYXQgd2UgZG9uJ3QgbWlzcyBvYnNlcnZhdGlvbnMgCmFjcm9zcyBtb2RlbHMuCgpgYGB7cn0KIyBSZS1jb25maWd1cmUgbWVyZ2VkIGRhdGEgdG8gdXNlIG1heCBjdXRvZmYgKDEwMCBrbSkKYWdnIDwtICJpZHciCmN1dCA8LSAxMDAKZGF0X21lcmdlZDEgPC0gZGF0X21lcmdlZCAlPiUgZmlsdGVyKGFnZ19tZXRob2QgPT0gYWdnLCBjdXRvZmYgPT0gY3V0KQpgYGAKCldlIGNoYW5nZSBvdXIgYmFzZWxpbmUgbW9kZWwgdG8gIAoqICRQTV97Mi41fSBcc2ltIEFPVF97YW5vbX0gKyBzbW9rZWRheSArIEFPVF97YW5vbX0gXHRpbWVzIHNtb2tlZGF5ICsgc3RhdGlvbkZFICsgc2FtcGxlbW9udGhGRSQgIAphbmQgY29tcGFyZSB3aXRoICAKKiAkUE1fezIuNX0gXHNpbSBBT1Rfe2Fub219ICsgc21va2VkYXkgKyBBT1Rfe2Fub219IFx0aW1lcyBzbW9rZWRheSArIEFPVF97YW5vbX0gXHRpbWVzIHNtb2tlZGF5IFx0aW1lcyBoZWlnaHQgKyBzdGF0aW9uRkUgKyBzYW1wbGVtb250aEZFJCAgCiogJFBNX3syLjV9IFxzaW0gQU9UX3thbm9tfSArIHNtb2tlZGF5ICsgQU9UX3thbm9tfSBcdGltZXMgc21va2VkYXkgKyBBT1Rfe2Fub219IFx0aW1lcyBzbW9rZWRheSBcdGltZXMgcHJlc3N1cmUgKyBzdGF0aW9uRkUgKyBzYW1wbGVtb250aEZFJCAgCgpgYGB7cn0KZGYgPC0gZGF0X21lcmdlZDEKbWRsMCA8LSBmZW9scyhwbTI1IH4gYW90X2Fub20gKyBzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXkgfCBpZF9lcGEgKyBtb250aCwKICAgICAgICAgICAgICBkYXRhID0gZGYpCm1kbDEgPC0gZmVvbHMocG0yNSB+IGFvdF9hbm9tICsgc21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5OmhlaWdodCB8IGlkX2VwYSArIG1vbnRoLAogICAgICAgICAgICAgIGRhdGEgPSBkZikKbWRsMiA8LSBmZW9scyhwbTI1IH4gYW90X2Fub20gKyBzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXk6cHJlc3N1cmUgfCBpZF9lcGEgKyBtb250aCwKICAgICAgICAgICAgICBkYXRhID0gZGYpCmBgYAoKV2l0aGluIFIyIGlzIGFyb3VuZCAwLjM0IGFuZCBpcyBoaWdoZXN0IGZvciBtb2RlbCB3aXRoIGhlaWdodCwgdGhvdWdoIHVsdGltYXRlbHkKbm90IGRpZmZlcmluZyBtdWNoIGFjcm9zcyBtb2RlbHMuIFNtb2tlIGRheSB0ZXJtIGFuZCBwcmVzc3VyZSBpbnRlcmFjdGlvbiB0ZXJtIAphcmUgbm90IHNpZ25pZmljYW50LiBJbnRlcmFjdGlvbiB0ZXJtIHdpdGggaGVpZ2h0IGlzIHNpZ25pZmljYW50LCBhbmQgY29lZmZpY2llbnQgCmdvZXMgaW4gZXhwZWN0ZWQgZGlyZWN0aW9uIGJ1dCBpcyBzbWFsbC4KCmBgYHtyfQpldGFibGUobWRsMCwgbWRsMSwgbWRsMiwgZHJvcC5zZWN0aW9uID0gImZpeGVmIikKYGBgCgpMZXQncyBnZXQgcHJlZGljdGVkIFBNMi41IGFuZCBwbG90IHNvbWUgY29tcGFyaXNvbnMuCgpgYGB7cn0KZGYkcHJlZF9hb3Rhbm9tX3Ntb2tlZGF5IDwtIHByZWRpY3QobWRsMCkKZGYkcHJlZF9hb3Rhbm9tX3Ntb2tlZGF5X2hlaWdodCA8LSBwcmVkaWN0KG1kbDEpCmRmJHByZWRfYW90YW5vbV9zbW9rZWRheV9wcmVzc3VyZSA8LSBwcmVkaWN0KG1kbDIpCmBgYAoKV2UgZmlsdGVyIHRvIHNtb2tlIGRheXMgYW5kIGNvbXBhcmUgdGhlIHByZWRpY3Rpb25zIG9mIGVhY2ggbW9kZWwgdG8gb2JzZXJ2ZWQKUE0yLjUgdmFsdWVzLiBUaGV5IGxvb2sgdmVyeSBtdWNoIHRoZSBzYW1lLiBTZWVtcyB0aGVyZSdzIHN0aWxsIHF1aXRlIHNvbWUKdW5leHBsYWluZWQgdmFyaWF0aW9uIGluIFBNMi41LgoKYGBge3J9CmRmc2QgPC0gZGYgJT4lIAogIGZpbHRlcihzbW9rZV9kYXkgPT0gMSkgJT4lIAogIHBpdm90X2xvbmdlcihzdGFydHNfd2l0aCgicHJlZF8iKSwgbmFtZXNfdG8gPSAibWRsIikKZ2dwbG90KGRhdGEgPSBkZnNkLCBtYXBwaW5nID0gYWVzKHggPSB2YWx1ZSwgeSA9IHBtMjUpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gbWRsKSwgYWxwaGEgPSAwLjIpICsgCiAgZmFjZXRfd3JhcCh2YXJzKG1kbCkpCmBgYAoKSGVyZSdzIGEgY29ycmVsYXRpb24gcGxvdCB0byBjb25maXJtIHRoYXQgdGhlIHByZWRpY3Rpb25zIGFyZSB2ZXJ5IGNsb3NlIGFjcm9zcwp0aGUgbW9kZWxzLgoKYGBge3J9CmRmc2QgPC0gZGYgJT4lIAogIGZpbHRlcihzbW9rZV9kYXkgPT0gMSkgJT4lIAogIHNlbGVjdChwbTI1LCBzdGFydHNfd2l0aCgicHJlZF8iKSkKY29ycl9kZnNkIDwtIGNvcihkZnNkLCB1c2UgPSAiY29tcGxldGUub2JzIikKZ2djb3JycGxvdChjb3JyX2Rmc2QsIGxhYiA9IFRSVUUpCmBgYAoKTGV0J3MgdHJ5IHBsb3R0aW5nIHRpbWUgc2VyaWVzIHdoZXJlIGJhc2VsaW5lIG1vZGVsIHVuZGVyZXN0aW1hdGVzLgoKTG9va3MgbGlrZSBNb25vIGNvdW50eSBQTTIuNSBnZXRzIHVuZGVyZXN0aW1hdGVkIHRoZSBtb3N0LgoKYGBge3J9CmRmJHJlc2lkMCA8LSBtZGwwJHJlc2lkdWFscwpkZiAlPiUgc2xpY2VfbWF4KHJlc2lkMCwgbiA9IDUwKSAlPiUgY291bnQoY291bnR5KSAlPiUgYXJyYW5nZShkZXNjKG4pKQpgYGAKClRoZSBbU2xpbmsgRmlyZV0oaHR0cHM6Ly9pbmNpd2ViLm53Y2cuZ292L2luY2lkZW50LzcxMDUvKSBpbiBNb25vIGNvdW50eSB3YXMgCnJlcG9ydGVkIG9uIEF1Z3VzdCAyOXRoIGFuZCBsYXN0IHVwZGF0ZWQgT2N0b2JlciAyMm5kLiBTbyB3ZSBwbG90IHRoZSB0aW1lIHNlcmllcyAKZnJvbSBtaWQtQXVndXN0IHRocm91Z2ggdGhlIGVuZCBvZiBPY3RvYmVyLgoKYGBge3J9CnQxIDwtIGFzLkRhdGUoIjIwMjAtMDgtMTUiKQp0MiA8LSBhcy5EYXRlKCIyMDIwLTEwLTMxIikKZXZlbnQgPC0gZGYgJT4lIAogIGZpbHRlcih0MSA8PSBkYXRlLCBkYXRlIDw9IHQyLCBjb3VudHkgPT0gIk1vbm8iKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYyhwbTI1LCBzdGFydHNfd2l0aCgicHJlZCIpKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lIAogIHBpdm90X2xvbmdlcihjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkXyIpKSwgbmFtZXNfdG8gPSAibWRsIikKCmdncGxvdChkYXRhID0gZXZlbnQsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvciA9IG1kbCkpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpJbiBhZGRpdGlvbiB0byB0aGUgYmFzZWxpbmUgbW9kZWwgdW5kZXJlc3RpbWF0aW5nIFBNMi41LCBvdXIgbW9kZWxzIHdpdGggaGVpZ2h0IApvciBwcmVzc3VyZSBkb24ndCBxdWl0ZSBjYXB0dXJlIG1vc3Qgc3Bpa2VzLCBub3IgZG8gdGhleSBkaWZmZXIgbXVjaCBmcm9tIHRoZSAKYmFzZWxpbmUgbW9kZWwuCgpMZXQncyBhbHNvIHRha2UgYSBsb29rIGF0IG91ciBydW5uZXItdXAsIExhbmUgY291bnR5LiAgClRoZSBbSG9saWRheSBGYXJtIEZpcmVdKGh0dHBzOi8vaW5jaXdlYi5ud2NnLmdvdi9pbmNpZGVudC83MTcwLykgd2FzIHJlcG9ydGVkIG9uIApTZXB0ZW1iZXIgN3RoIGFuZCBjb250YWluZWQgb24gT2N0b2JlciAyOXRoLiBTbyB3ZSBwbG90IHRoZSB0aW1lIHNlcmllcyBmcm9tIGxhdGUgCkF1Z3VzdCB0byBtaWQtTm92ZW1iZXIuCgpgYGB7cn0KdDEgPC0gYXMuRGF0ZSgiMjAyMC0wOC0yMiIpCnQyIDwtIGFzLkRhdGUoIjIwMjAtMTEtMTUiKQpldmVudCA8LSBkZiAlPiUgCiAgZmlsdGVyKHQxIDw9IGRhdGUsIGRhdGUgPD0gdDIsIGNvdW50eSA9PSAiTGFuZSIpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXplKGFjcm9zcyhjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkIikpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMocG0yNSwgc3RhcnRzX3dpdGgoInByZWRfIikpLCBuYW1lc190byA9ICJtZGwiKQoKZ2dwbG90KGRhdGEgPSBldmVudCwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IHZhbHVlKSkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKGNvbG9yID0gbWRsKSkgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCk91ciBtb2RlbHMgdW5kZXJlc3RpbWF0ZSBQTTIuNSwgYnV0IGl0J3Mgbm90IGFzIGJhZCBhcyBhYm92ZS4gQWxzbyBvdmVyZXN0aW1hdGVzClBNMi41IGxldmVscyBuZWFyIHplcm8uIE1vZGVsIHdpdGggaGVpZ2h0IGNhcHR1cmVzIHNwaWtlIGp1c3QgYmVmb3JlIGhpdHRpbmcKdGhlIHRvcCBvZiB0aGUgYmlnIHNwaWtlIHNsaWdodGx5IGJldHRlciB0aGFuIHRoZSBvdGhlcnMuCgpMZXQncyB0YWtlIGEgbG9vayBhdCBhIGJpZyBmaXJlIGFuZCBzZWUgaG93IG91ciBtb2RlbHMgcGVyZm9ybS4gIApUaGUgW0F1Z3VzdCBDb21wbGV4XShodHRwczovL2luY2l3ZWIubndjZy5nb3YvaW5jaWRlbnQvNjk4My8pIHN0YXJ0ZWQgQXVndXN0IDE3dGgKYW5kIHdhcyBjb250YWluZWQgb24gTm92ZW1iZXIgMTV0aC4gSXQgW2J1cm5lZF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQXVndXN0X0NvbXBsZXhfZmlyZSkgCmluIEdsZW5uLCBMYWtlLCBNZW5kb2Npbm8sIFRlaGFtYSwgVHJpbml0eSwgYW5kIFNoYXN0YSBjb3VudGllcy4gU28gd2UgcGxvdCB0aGUgCnRpbWUgc2VyaWVzIGZyb20gZWFybHkgQXVndXN0IHRvIHRoZSBlbmQgb2YgTm92ZW1iZXIuCgpgYGB7cn0KdDEgPC0gYXMuRGF0ZSgiMjAyMC0wOC0wMSIpCnQyIDwtIGFzLkRhdGUoIjIwMjAtMTEtMzAiKQpldmVudCA8LSBkZiAlPiUgCiAgZmlsdGVyKHQxIDw9IGRhdGUsIGRhdGUgPD0gdDIsIAogICAgICAgICBjb3VudHkgJWluJSBjKCJHbGVubiIsICJMYWtlIiwgIk1lbmRvY2lubyIsICJUZWhhbWEiLCAiVHJpbml0eSIsICJTaGFzdGEiKSkgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JSAKICBzdW1tYXJpemUoYWNyb3NzKGMocG0yNSwgc3RhcnRzX3dpdGgoInByZWQiKSksIG1lYW4sIG5hLnJtID0gVFJVRSkpICU+JSAKICBwaXZvdF9sb25nZXIoYyhwbTI1LCBzdGFydHNfd2l0aCgicHJlZF8iKSksIG5hbWVzX3RvID0gIm1kbCIpCgpnZ3Bsb3QoZGF0YSA9IGV2ZW50LCBtYXBwaW5nID0gYWVzKHggPSBkYXRlLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoY29sb3IgPSBtZGwpKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKT3VyIG1vZGVscyBjYXB0dXJlIHRoZSBiaWcgc3Bpa2UgYnV0IG5vdGljZWFibHkgb3ZlcmVzdGltYXRlIHRoZSBzcGlrZSBpbiBsYXRlIApBdWd1c3QuIFRoZXkgYWxzbyB1bmRlcmVzdGltYXRlIGVhcmx5IEF1Z3VzdCBhbmQgb3ZlcmVzdGltYXRlIGEgYml0IGluIGVhcmx5IApTZXB0ZW1iZXIuIFRoZSBtb2RlbCBwcmVkaWN0aW9ucyBvdmVybGFwIGEgbG90LCBidXQgYXQgYSBmZXcgc3Bpa2VzIHRoZSBtb2RlbAp3aXRoIGhlaWdodCBzZWVtcyB0byBlc3RpbWF0ZSBhIGJpdCBoaWdoZXIgdGhhbiB0aGUgb3RoZXJzLgoKUGVyaGFwcyB0aGVyZSBhcmUgYmV0dGVyIHdheXMgb2YgYW5hbHl6aW5nIHRoaXMsIGJ1dCBiYXNlZCBvbiBSMiBhbmQgcGxvdHMsIGl0CnNlZW1zIHRvIG1lIHRoYXQgaGVpZ2h0IGlzbid0IG1ha2luZyBhIGJpZyBkaWZmZXJlbmNlIGZyb20gb3VyIGJhc2VsaW5lIG1vZGVsLgoKIyMgVXBkYXRlIDIKCldlIGNoYW5nZSB0byAzMCBrbSBjdXRvZmYgYW5kIGZpdCBvdXIgYmFzZWxpbmUgbW9kZWwgYW5kIHRoZSBtb2RlbCB3aXRoIGhlaWdodAp1c2luZyB0aGUgc2FtZSBzYW1wbGUuCgpgYGB7cn0KIyBSZS1jb25maWd1cmUgbWVyZ2VkIGRhdGEgdG8gdXNlIDMwIGttIGN1dG9mZgphZ2cgPC0gImlkdyIKY3V0IDwtIDMwCmRhdF9tZXJnZWQxIDwtIGRhdF9tZXJnZWQgJT4lIGZpbHRlcihhZ2dfbWV0aG9kID09IGFnZywgY3V0b2ZmID09IGN1dCkKCiMgRHJvcCBvYnMgd2hlcmUgbWlzc2luZyBoZWlnaHQKZGF0X21lcmdlZDEgPC0gZGF0X21lcmdlZDEgJT4lIGRyb3BfbmEoaGVpZ2h0KQpgYGAKCmBgYHtyfQpkZiA8LSBkYXRfbWVyZ2VkMQptZGwwIDwtIGZlb2xzKHBtMjUgfiBhb3RfYW5vbSArIHNtb2tlX2RheSArIGFvdF9hbm9tOnNtb2tlX2RheSB8IGlkX2VwYSArIG1vbnRoLAogICAgICAgICAgICAgIGRhdGEgPSBkZikKbWRsMSA8LSBmZW9scyhwbTI1IH4gYW90X2Fub20gKyBzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXk6aGVpZ2h0IHwgaWRfZXBhICsgbW9udGgsCiAgICAgICAgICAgICAgZGF0YSA9IGRmKQptZGwyIDwtIGZlb2xzKHBtMjUgfiBhb3RfYW5vbSArIHNtb2tlX2RheSArIGFvdF9hbm9tOnNtb2tlX2RheSArIGFvdF9hbm9tOnNtb2tlX2RheTpwcmVzc3VyZSB8IGlkX2VwYSArIG1vbnRoLAogICAgICAgICAgICAgIGRhdGEgPSBkZikKYGBgCgpDb21wYXJlZCB0byB1c2luZyB0aGUgMTAwIGttIGN1dG9mZiwgd2l0aGluIFIyIGlzIGEgYml0IGxvd2VyIGhlcmUuIFRoZSBtb2RlbCAKd2l0aCBoZWlnaHQgZG9lcyBiZXN0LCB0aG91Z2ggUjIgZGlmZmVyZW5jZXMgYXJlIHNtYWxsLiBDb2VmZmljaWVudCBvbiB0ZXJtIHdpdGggCmhlaWdodCBzZWVtcyBzaW1pbGFyIHRvIGFib3ZlLCBpLmUuIHJpZ2h0IGRpcmVjdGlvbiBidXQgc21hbGwuIENvZWZmaWNpZW50IG9uIAp0ZXJtIHdpdGggaGVpZ2h0IGlzIHNtYWxsZXIgaW4gbWFnbml0dWRlIG5vdy4gQWxzbyBqdXN0IG5vdGljZWQgdGhhdCBjb2VmZmljaWVudCAKb24ganVzdCBzbW9rZSBkYXkgdGVybSBpcyBuZWdhdGl2ZSwgbm90IHN1cmUgaWYgdGhhdCBtYWtlcyBzZW5zZS4KCmBgYHtyfQpldGFibGUobWRsMCwgbWRsMSwgbWRsMiwgZHJvcC5zZWN0aW9uID0gImZpeGVmIikKYGBgCgpMZXQncyBjb21wYXJlIHRoZSBtb2RlbHMnIHByZWRpY3RlZCBQTTIuNS4KCmBgYHtyfQpkZiRwcmVkX2FvdGFub21fc21va2VkYXkgPC0gcHJlZGljdChtZGwwKQpkZiRwcmVkX2FvdGFub21fc21va2VkYXlfaGVpZ2h0IDwtIHByZWRpY3QobWRsMSkKZGYkcHJlZF9hb3Rhbm9tX3Ntb2tlZGF5X3ByZXNzdXJlIDwtIHByZWRpY3QobWRsMikKYGBgCgpXZSBmaWx0ZXIgdG8gc21va2UgZGF5cyBhbmQgY29tcGFyZSB0aGUgcHJlZGljdGlvbnMgdXNpbmcgdGhlaXIgY29ycmVsYXRpb25zLgoKYGBge3J9CmRmc2QgPC0gZGYgJT4lIAogIGZpbHRlcihzbW9rZV9kYXkgPT0gMSkgJT4lIAogIHNlbGVjdChwbTI1LCBzdGFydHNfd2l0aCgicHJlZF8iKSkKY29ycl9kZnNkIDwtIGNvcihkZnNkLCB1c2UgPSAiY29tcGxldGUub2JzIikKZ2djb3JycGxvdChjb3JyX2Rmc2QsIGxhYiA9IFRSVUUpCmBgYAoKRG9lc24ndCBzZWVtIGxpa2UgMzAga20gY3V0b2ZmIGhlbHBlZCBoZWlnaHQgaGF2ZSBsYXJnZXIgZWZmZWN0LgoKSSB3b25kZXIgaG93IG11Y2ggdGhlIGNob2ljZSBvZiBpbml0aWFsIGhlaWdodCBwYXJhbWV0ZXJzIGluZmx1ZW5jZXMgb3VyIHJlc3VsdHMuCgpBZGRpbmcgaGVyZSB0aW1lIHNlcmllcyBwbG90IG9mIEF1Z3VzdCBDb21wbGV4IHdpdGggb25seSBvYnNlcnZlZCBQTTIuNSBhbmQgCmJhc2VsaW5lIG1vZGVsIHRvIGhlbHAgc2VlIGxpbmVzIG1vcmUgY2xlYXJseS4KCmBgYHtyfQp0MSA8LSBhcy5EYXRlKCIyMDIwLTA4LTAxIikKdDIgPC0gYXMuRGF0ZSgiMjAyMC0xMS0zMCIpCmV2ZW50IDwtIGRmICU+JSAKICBzZWxlY3QoLXByZWRfYW90YW5vbV9zbW9rZWRheV9oZWlnaHQsIC1wcmVkX2FvdGFub21fc21va2VkYXlfcHJlc3N1cmUpICU+JSAKICBmaWx0ZXIodDEgPD0gZGF0ZSwgZGF0ZSA8PSB0MiwgCiAgICAgICAgIGNvdW50eSAlaW4lIGMoIkdsZW5uIiwgIkxha2UiLCAiTWVuZG9jaW5vIiwgIlRlaGFtYSIsICJUcmluaXR5IiwgIlNoYXN0YSIpKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYyhwbTI1LCBzdGFydHNfd2l0aCgicHJlZCIpKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lIAogIHBpdm90X2xvbmdlcihjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkXyIpKSwgbmFtZXNfdG8gPSAibWRsIikKCmdncGxvdChkYXRhID0gZXZlbnQsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvciA9IG1kbCkpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgojIyBVcGRhdGUgMwoKIyMjIExpbWl0IHRvIENhbGlmb3JuaWEKCldlIGxpbWl0IHRvIEVQQSBzdGF0aW9ucyBpbiBDYWxpZm9ybmlhIHNpbmNlIHRyYWplY3RvcmllcyB3ZXJlIGluaXRpYXRlZCBpbgpDYWxpZm9ybmlhIGFuZCBtYXkgbm90IHJlcHJlc2VudCBwbHVtZXMgaW4gb3RoZXIgc3RhdGVzLgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCByZXN1bHRzID0gImhpZGUifQojIERyb3Agb2JzIG5vdCBpbiBDYWxpZm9ybmlhCmxvY19lcGEgPC0gZGF0X21lcmdlZFtjKCJsb25fZXBhIiwgImxhdF9lcGEiKV0gJT4lIGRpc3RpbmN0KCkKdXNfc3RhdGVzIDwtIHN0YXRlcygpICU+JSBhcygiU3BhdGlhbCIpCnVzX3N0YXRlcyA8LSBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUoU3BhdGlhbFBvbHlnb25zKHVzX3N0YXRlc0Bwb2x5Z29ucyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzX3N0YXRlc0BkYXRhICU+JSBzZWxlY3QoTkFNRSkpCm8gPC0gb3ZlcihTcGF0aWFsUG9pbnRzKGxvY19lcGEpLCB1c19zdGF0ZXMpCmRhdF9tZXJnZWQwIDwtIGRhdF9tZXJnZWQgJT4lIAogIGxlZnRfam9pbihiaW5kX2NvbHMobG9jX2VwYSwgbykpICU+JSAKICByZW5hbWUoc3RhdGUgPSBOQU1FKSAlPiUgCiAgZmlsdGVyKHN0YXRlID09ICJDYWxpZm9ybmlhIikKYGBgCgpgYGB7cn0KYWdnIDwtICJpZHciCmN1dCA8LSAzMApkYXRfbWVyZ2VkMSA8LSBkYXRfbWVyZ2VkMCAlPiUgZmlsdGVyKGFnZ19tZXRob2QgPT0gYWdnLCBjdXRvZmYgPT0gY3V0KQpkYXRfbWVyZ2VkMSA8LSBkYXRfbWVyZ2VkMSAlPiUgZHJvcF9uYShoZWlnaHQpCmBgYAoKYGBge3J9CmRmIDwtIGRhdF9tZXJnZWQxCm1kbDAgPC0gZmVvbHMocG0yNSB+IGFvdF9hbm9tICsgc21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5IHwgaWRfZXBhICsgbW9udGgsCiAgICAgICAgICAgICAgZGF0YSA9IGRmKQptZGwxIDwtIGZlb2xzKHBtMjUgfiBhb3RfYW5vbSArIHNtb2tlX2RheSArIGFvdF9hbm9tOnNtb2tlX2RheSArIGFvdF9hbm9tOnNtb2tlX2RheTpoZWlnaHQgfCBpZF9lcGEgKyBtb250aCwKICAgICAgICAgICAgICBkYXRhID1kZikKbWRsMiA8LSBmZW9scyhwbTI1IH4gYW90X2Fub20gKyBzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXk6cHJlc3N1cmUgfCBpZF9lcGEgKyBtb250aCwKICAgICAgICAgICAgICBkYXRhID0gZGYpCmBgYAoKTG9zdCBhIGxpdHRsZSBvdmVyIGhhbGYgb3VyIG9icyB0byBmaWx0ZXJpbmcgZm9yIENhbGlmb3JuaWEuIFNtb2tlIGRheSBhbG9uZSAKY29lZmZpY2llbnQgaXMgcG9zaXRpdmUgbm93LiBDb21wYXJlZCB0byBhYm92ZSwgY29lZmZpY2llbnQgb24gYW5vbWFsb3VzIEFPVCBpcyAKYmlnZ2VyIGFuZCBjb2VmZmljaWVudCBvbiBhbm9tYWxvdXMgQU9UIGFuZCBzbW9rZSBkYXkgaW50ZXJhY3Rpb24gdGVybSBpcyAKc21hbGxlciBmb3IgYmFzZWxpbmUgYW5kIGhlaWdodCBtb2RlbHMgYW5kIGxhcmdlciBmb3IgcHJlc3N1cmUgbW9kZWwuIApJbnRlcmFjdGlvbiB0ZXJtIHdpdGggaGVpZ2h0IGlzIG5vIGxvbmdlciBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBXaXRoaW4gUjIgCmlzIGxvd2VyIG5vdy4gTW9kZWwgd2l0aCBoZWlnaHQgc3RpbGwgcGVyZm9ybXMgYmV0dGVyIHRoYW4gYmFzZWxpbmUgbW9kZWwsIGJ1dCAKUjIgZGlmZmVyZW5jZXMgYXJlIHNtYWxsLCBhbmQgbm93IG1vZGVsIHdpdGggcHJlc3N1cmUgcGVyZm9ybXMgYmVzdC4KCmBgYHtyfQpldGFibGUobWRsMCwgbWRsMSwgbWRsMiwgZHJvcC5zZWN0aW9uID0gImZpeGVmIikKYGBgCgpXZSBnZXQgdGhlIG1vZGVscycgcHJlZGljdGVkIFBNMi41LiBXZSBzdGljayB0byB0b3RhbCBQTTIuNSBmb3Igbm93LiBQbG90cyBvZgpzcGVjaWZpY2FsbHkgc21va2UgUE0yLjUgd2lsbCBjb21lIGluIG5leHQgc2VjdGlvbi4KCmBgYHtyfQpkZiRwcmVkX2FvdGFub21fc21va2VkYXkgPC0gcHJlZGljdChtZGwwKQpkZiRwcmVkX2FvdGFub21fc21va2VkYXlfaGVpZ2h0IDwtIHByZWRpY3QobWRsMSkKZGYkcHJlZF9hb3Rhbm9tX3Ntb2tlZGF5X3ByZXNzdXJlIDwtIHByZWRpY3QobWRsMikKYGBgCgpQcmVkaWN0aW9ucyBzdGlsbCB2ZXJ5IHNpbWlsYXIsIHRob3VnaCBtb2RlbCB3aXRoIHByZXNzdXJlIG1vcmUgZGlmZmVyZW50IHRoYW4KbW9kZWwgd2l0aCBoZWlnaHQgd2hlbiBjb21wYXJlZCBhZ2FpbnN0IGJhc2VsaW5lLgoKYGBge3J9CmRmc2QgPC0gZGYgJT4lIAogIGZpbHRlcihzbW9rZV9kYXkgPT0gMSkgJT4lIAogIHNlbGVjdChwbTI1LCBzdGFydHNfd2l0aCgicHJlZF8iKSkKY29ycl9kZnNkIDwtIGNvcihkZnNkLCB1c2UgPSAiY29tcGxldGUub2JzIikKZ2djb3JycGxvdChjb3JyX2Rmc2QsIGxhYiA9IFRSVUUpCmBgYAoKUGxvdHRpbmcgdGltZSBzZXJpZXMgZm9yIEJheSBBcmVhIGFuZXcuCgpgYGB7cn0KZXZlbnQgPC0gZGYgJT4lIAogIGZpbHRlcih0MSA8PSBkYXRlLCBkYXRlIDw9IHQyLCBjYnNhX25hbWUgPT0gIlNhbiBGcmFuY2lzY28tT2FrbGFuZC1IYXl3YXJkLCBDQSIpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXplKGFjcm9zcyhjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkIikpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMocG0yNSwgc3RhcnRzX3dpdGgoInByZWRfIikpLCBuYW1lc190byA9ICJtZGwiKQoKZ2dwbG90KGRhdGEgPSBldmVudCwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IHZhbHVlKSkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKGNvbG9yID0gbWRsKSkgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCk1vZGVscyB1bmRlcmVzdGltYXRlIHRoZSBiaWcgcGVhaywgYnV0IG1vZGVsIHdpdGggaGVpZ2h0IGFuZCBiYXNlbGluZSBnZXQgYSBiaXQgCm1vcmUgb2YgdGhlIHRvcCBzcGlrZSB0aGFuIGRvZXMgbW9kZWwgd2l0aCBwcmVzc3VyZS4gTWVkaXVtIHBlYWtzIHNvbWV3aGF0IApjYXB0dXJlZC4gTG93ZXIgbGV2ZWxzIG9mIFBNMi41IGdldCBvdmVyZXN0aW1hdGVkIGEgYml0LgoKTW9ubyBjb3VudHkgZ2V0cyB1bmRlcmVzdGltYXRlZCB0aGUgbW9zdCBhZ2Fpbi4KCmBgYHtyfQpkZiRyZXNpZDAgPC0gbWRsMCRyZXNpZHVhbHMKZGYgJT4lIHNsaWNlX21heChyZXNpZDAsIG4gPSA1MCkgJT4lIGNvdW50KGNvdW50eSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgpTbGluayBGaXJlIGFnYWluLgoKYGBge3J9CnQxIDwtIGFzLkRhdGUoIjIwMjAtMDgtMTUiKQp0MiA8LSBhcy5EYXRlKCIyMDIwLTEwLTMxIikKZXZlbnQgPC0gZGYgJT4lIAogIGZpbHRlcih0MSA8PSBkYXRlLCBkYXRlIDw9IHQyLCBjb3VudHkgPT0gIk1vbm8iKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYyhwbTI1LCBzdGFydHNfd2l0aCgicHJlZCIpKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lIAogIHBpdm90X2xvbmdlcihjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkXyIpKSwgbmFtZXNfdG8gPSAibWRsIikKCmdncGxvdChkYXRhID0gZXZlbnQsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvciA9IG1kbCkpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpNb2RlbCB3aXRoIHByZXNzdXJlIGNhcHR1cmVzIHBlYWtzIGEgYml0IGJldHRlci4gQWxsIG1vZGVscyBvdmVyZXN0aW1hdGUgbG93CmxldmVscyBvZiBQTTIuNS4KCkF1Z3VzdCBDb21wbGV4IGFnYWluLgoKYGBge3J9CnQxIDwtIGFzLkRhdGUoIjIwMjAtMDgtMDEiKQp0MiA8LSBhcy5EYXRlKCIyMDIwLTExLTMwIikKZXZlbnQgPC0gZGYgJT4lIAogIGZpbHRlcih0MSA8PSBkYXRlLCBkYXRlIDw9IHQyLCAKICAgICAgICAgY291bnR5ICVpbiUgYygiR2xlbm4iLCAiTGFrZSIsICJNZW5kb2Npbm8iLCAiVGVoYW1hIiwgIlRyaW5pdHkiLCAiU2hhc3RhIikpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXplKGFjcm9zcyhjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkIikpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMocG0yNSwgc3RhcnRzX3dpdGgoInByZWRfIikpLCBuYW1lc190byA9ICJtZGwiKQoKZ2dwbG90KGRhdGEgPSBldmVudCwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IHZhbHVlKSkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKGNvbG9yID0gbWRsKSkgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCk1vZGVsIHdpdGggcHJlc3N1cmUgY2FwdHVyZXMgYmlnIHBlYWsgYmVzdC4gQWxsIG1vZGVscyBvdmVyZXN0aW1hdGUgbGF0ZSBBdWd1c3QKcGVhayBhbmQgdW5kZXJlc3RpbWF0ZSBsYXRlIFNlcHRlbWJlciBwZWFrcy4KCiMjIyBJRFcgYXZlcmFnZSB3aXRoaW4tdHJhamVjdG9yeSBtaW5pbXVtIGhlaWdodAoKTm93IHdlIHVzZSBJRFcgYXZlcmFnZSBvZiBkYWlseSB3aXRoaW4tdHJhamVjdG9yeSBtaW5pbXVtIGhlaWdodHMgd2l0aGluIGJ1ZmZlciAKem9uZS4gSW4gb3RoZXIgd29yZHMsIGZvciBlYWNoIEVQQSBzdGF0aW9uLWRheS1QTTIuNSBjb21iaW5hdGlvbiwgZmlyc3QgZm91bmQgCnRyYWplY3RvcnkgcG9pbnRzIG9uIHRoZSBzYW1lIGRheSB3aXRoaW4gYSBzZXQgcmFkaXVzLCBuZXh0IGdyb3VwZWQgYnkgCnRyYWplY3RvcnkgSUQgYW5kIGZvdW5kIHRyYWplY3RvcnkgcG9pbnQgd2l0aCBtaW5pbXVtIGhlaWdodCAobWF4aW11bSBwcmVzc3VyZSkgCmZvciBlYWNoIHRyYWplY3RvcnksIGFuZCB0aGVuIElEVyBhdmVyYWdlIHRoZSBoZWlnaHQgKHByZXNzdXJlKSB2YWx1ZXMgYWNyb3NzIAp0cmFqZWN0b3JpZXMuCgpgYGB7cn0KIyBOZXcgb3B0aW9uICJleHRyIiBmb3IgdXNpbmcgd2l0aGluLXRyYWplY3RvcnkgZXh0cmVtYQphZ2cgPC0gImV4dHIiCmN1dCA8LSAzMApkYXRfbWVyZ2VkMSA8LSBkYXRfbWVyZ2VkMCAlPiUgZmlsdGVyKGFnZ19tZXRob2QgPT0gYWdnLCBjdXRvZmYgPT0gY3V0KQpkYXRfbWVyZ2VkMSA8LSBkYXRfbWVyZ2VkMSAlPiUgZHJvcF9uYShoZWlnaHQpCmBgYAoKYGBge3J9CmRmIDwtIGRhdF9tZXJnZWQxCm1kbDAgPC0gZmVvbHMocG0yNSB+IGFvdF9hbm9tICsgc21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5IHwgaWRfZXBhICsgbW9udGgsCiAgICAgICAgICAgICAgZGF0YSA9IGRmKQptZGwxIDwtIGZlb2xzKHBtMjUgfiBhb3RfYW5vbSArIHNtb2tlX2RheSArIGFvdF9hbm9tOnNtb2tlX2RheSArIGFvdF9hbm9tOnNtb2tlX2RheTpoZWlnaHQgfCBpZF9lcGEgKyBtb250aCwKICAgICAgICAgICAgICBkYXRhID0gZGYpCm1kbDIgPC0gZmVvbHMocG0yNSB+IGFvdF9hbm9tICsgc21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5OnByZXNzdXJlIHwgaWRfZXBhICsgbW9udGgsCiAgICAgICAgICAgICAgZGF0YSAgPSBkZikKYGBgCgpSZXN1bHRzIGFyZSBwcmV0dHkgc2ltaWxhciB0byBhYm92ZS4gU28gdGhlIGNoYW5nZSBpbiBtYXRjaGluZyBtZXRob2QgZG9lc24ndCAKc2VlbSB0byBtYWtlIGEgYmlnIGRpZmZlcmVuY2UuCgpgYGB7cn0KZXRhYmxlKG1kbDAsIG1kbDEsIG1kbDIsIGRyb3Auc2VjdGlvbiA9ICJmaXhlZiIpCmBgYAoKTm93IHdlIHByZWRpY3QgYW5kIHBsb3Qgc21va2UgUE0yLjUuIE5vdCBzdXJlIGlmIEZFcyBhcmUgYWxzbyBzdXBwb3NlZCB0byBnbyBpbiAKaGVyZSBzbyBqdXN0IG5vdCBpbmNsdWRpbmcgZm9yIG5vdy4KCmBgYHtyfQpzbW9rZV9kYXkgPC0gZGYkc21va2VfZGF5CmFvdF9hbm9tIDwtIGRmJGFvdF9hbm9tCmhlaWdodCA8LSBkZiRoZWlnaHQKcHJlc3N1cmUgPC0gZGYkcHJlc3N1cmUKCmNvZWZzIDwtIGNvZWZmaWNpZW50cyhtZGwwKQpkZiRwcmVkX2FvdGFub21fc21va2VkYXkgPC0gY29lZnNbInNtb2tlX2RheSJdKnNtb2tlX2RheSArIAogIGNvZWZzWyJhb3RfYW5vbTpzbW9rZV9kYXkiXSphb3RfYW5vbSpzbW9rZV9kYXkKY29lZnMgPC0gY29lZmZpY2llbnRzKG1kbDEpCmRmJHByZWRfYW90YW5vbV9zbW9rZWRheV9oZWlnaHQgPC0gY29lZnNbInNtb2tlX2RheSJdKnNtb2tlX2RheSArIAogIGNvZWZzWyJhb3RfYW5vbTpzbW9rZV9kYXkiXSphb3RfYW5vbSpzbW9rZV9kYXkgKyAKICBjb2Vmc1siYW90X2Fub206c21va2VfZGF5OmhlaWdodCJdKmFvdF9hbm9tKnNtb2tlX2RheSpoZWlnaHQKY29lZnMgPC0gY29lZmZpY2llbnRzKG1kbDIpCmRmJHByZWRfYW90YW5vbV9zbW9rZWRheV9wcmVzc3VyZSA8LSBjb2Vmc1sic21va2VfZGF5Il0qc21va2VfZGF5ICsgCiAgY29lZnNbImFvdF9hbm9tOnNtb2tlX2RheSJdKmFvdF9hbm9tKnNtb2tlX2RheSArIAogIGNvZWZzWyJhb3RfYW5vbTpzbW9rZV9kYXk6cHJlc3N1cmUiXSphb3RfYW5vbSpzbW9rZV9kYXkqcHJlc3N1cmUKYGBgCgpXZSBmaWx0ZXIgdG8gc21va2UgZGF5cyBhbmQgY29tcGFyZSB0aGUgcHJlZGljdGlvbnMgb2YgZWFjaCBtb2RlbCB0byB0b3RhbCAKUE0yLjUgdmFsdWVzLiBQcmVzc3VyZSBtb2RlbCBzZWVtcyB0byBkbyBzbGlnaHRseSBiZXR0ZXIsIGJ1dCBwcmVkaWN0aW9ucyBkb24ndCAKZ2V0IHBhc3QgMTUwIG1pY3JvZ3JhbXMsIHNvIHNlZW1zIHdlIHByb2JhYmx5IHdvbid0IGJlIGFibGUgdG8gY2FwdHVyZSBiaWcgCnNwaWtlcy4KCmBgYHtyfQpkZnNkIDwtIGRmICU+JSAKICBmaWx0ZXIoc21va2VfZGF5ID09IDEpICU+JSAKICBwaXZvdF9sb25nZXIoc3RhcnRzX3dpdGgoInByZWRfIiksIG5hbWVzX3RvID0gIm1kbCIpCmdncGxvdChkYXRhID0gZGZzZCwgbWFwcGluZyA9IGFlcyh4ID0gdmFsdWUsIHkgPSBwbTI1KSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IG1kbCksIGFscGhhID0gMC4yKSArIAogIGZhY2V0X3dyYXAodmFycyhtZGwpKQpgYGAKCkJheSBBcmVhIGFnYWluLiBTaW5jZSB3ZSdyZSBwcmVkaWN0aW5nIHNtb2tlIFBNMi41IGFuZCBub3QgdG90YWwgUE0yLjUsIApwcmVkaWN0aW9ucyBhcmUgbG93ZXIgbm93LiBQcmVzc3VyZSBtb2RlbCBkb2VzIHdvcnNlIHRoYW4gYmFzZWxpbmUgYW5kIGhlaWdodCAKbW9kZWxzIGluIGxhdGUgU2VwdGVtYmVyL2Vhcmx5IE9jdG9iZXIsIGJ1dCBvdGhlcndpc2UgdGhlIG1vZGVscyBhcmUgcHJldHR5IApzaW1pbGFyLgoKYGBge3J9CmV2ZW50IDwtIGRmICU+JSAKICBmaWx0ZXIodDEgPD0gZGF0ZSwgZGF0ZSA8PSB0MiwgY2JzYV9uYW1lID09ICJTYW4gRnJhbmNpc2NvLU9ha2xhbmQtSGF5d2FyZCwgQ0EiKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYyhwbTI1LCBzdGFydHNfd2l0aCgicHJlZCIpKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lIAogIHBpdm90X2xvbmdlcihjKHBtMjUsIHN0YXJ0c193aXRoKCJwcmVkXyIpKSwgbmFtZXNfdG8gPSAibWRsIikKCmdncGxvdChkYXRhID0gZXZlbnQsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvciA9IG1kbCkpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpQcmVzc3VyZSBtb2RlbCBwcmVkaWN0aW9ucyBkaWZmZXIgZnJvbSBiYXNlbGluZSBtb2RlbCBwcmVkaWN0aW9ucyBieSBhdCBtb3N0IAphYm91dCA2MSBtaWNyb2dyYW1zLCB0aG91Z2ggdGhpcyBpcyB1bmNvbW1vbi4KCmBgYHtyfQpkaWZmX2hlaWdodCA8LSBkZiRwcmVkX2FvdGFub21fc21va2VkYXlfaGVpZ2h0IC0gZGYkcHJlZF9hb3Rhbm9tX3Ntb2tlZGF5CnN1bW1hcnkoZGlmZl9oZWlnaHQpCnF1YW50aWxlKGRpZmZfaGVpZ2h0LCBjKDAuMDEsIDAuMDUsIDAuOTUsIDAuOTkpKQoKZGlmZl9wcmVzc3VyZSA8LSBkZiRwcmVkX2FvdGFub21fc21va2VkYXlfcHJlc3N1cmUgLSBkZiRwcmVkX2FvdGFub21fc21va2VkYXkKc3VtbWFyeShkaWZmX3ByZXNzdXJlKQpxdWFudGlsZShkaWZmX3ByZXNzdXJlLCBjKDAuMDEsIDAuMDUsIDAuOTUsIDAuOTkpKQpgYGAKCkZyb20gdGhlIGFib3ZlLCBoZWlnaHQgZG9lc24ndCBnZW5lcmFsbHkgc2VlbSB0byBoZWxwIG11Y2guCgpBbHNvIHRvb2sgYSBsb29rIGF0IEJyZXkgZXQgYWwuIGFnYWluIGFuZCBhIGZldyBxdWVzdGlvbnMgcG9wcGVkIHVwIHNpbmNlIHdlIApmb2xsb3cgdGhlbSB1cCB0byBpbml0aWFsaXppbmcgdHJhamVjdG9yaWVzIGJ1dCBub3QgYWZ0ZXIgdGhhdDogIAotIFNob3VsZCB3ZSBzdGlsbCB0cnkgdG8gbWF0Y2ggdHJhamVjdG9yeSBwb2ludHMgdG8gcGx1bWVzPyBCcmV5IGV0IGFsLiB0aHJvd3MgCmF3YXkgdHJhamVjdG9yaWVzIHRoYXQgZG9uJ3Qgb3ZlcmxhcCBhIHBsdW1lIGluIHRoZSBmaXJzdCA0OCBob3Vycy4gQ3VycmVudGx5LCAKd2UgZG9uJ3QgZG8gYW55IG92ZXJsYXBwaW5nIHdpdGggcGx1bWUsIGJ1dCBpZiB0aGVyZSdzIGEgbG90IG9mIGZhbHNlIHBvc2l0aXZlcywgCml0IGNvdWxkIGhlbHAgdG8gb3ZlcmxhcCB0cmFqZWN0b3JpZXMgd2l0aCBwbHVtZXMgZmlyc3QuICAKLSBCcmV5IGV0IGFsLiB0aHJvd3MgYXdheSB0cmFqZWN0b3J5IHBvaW50cyBvbmNlIHRoZXkgcmVhY2ggMCBtZXRlcnMgaW4gaGVpZ2h0LiAKSSdtIG5vdCBzdXJlIHdoeSB0aGV5IGRvIHRoaXMsIGJ1dCBpcyB0aGlzIHNvbWV0aGluZyB3ZSBzaG91bGQgY29uc2lkZXIgZG9pbmc/IApJdCBpcyBwb3NzaWJsZSBmb3IgcGFydGljbGVzIHRvIHNpdCBvbiB0aGUgZ3JvdW5kIGZvciBhIHdoaWxlIGFuZCB0aGVuIHBpY2sgYmFjawp1cCBpbnRvIHRoZSBhdG1vc3BoZXJlICh2ZXJ5IGJyaWVmbHkgbG9va2VkIGludG8gdGhpcyBpbiB0aGUgZGF0YSBiZWZvcmUgb3V0IG9mIApjdXJpb3NpdHkpLgoKIyMgVXBkYXRlIDQKCkVQQSBzb21ldGltZXMgcmVjb3JkcyBtdWx0aXBsZSBQTTIuNSB2YWx1ZXMgZm9yIHRoZSBzYW1lIHN0YXRpb24tZGF5LCBzbyBsZXQncyAKdHJ5IGF2ZXJhZ2luZyBlYWNoIHN0YXRpb24tZGF5J3MgUE0yLjUgdmFsdWVzIGFuZCBzZWUgaWYgdGhhdCBjaGFuZ2VzIGFueXRoaW5nLgoKYGBge3J9CmFnZyA8LSAiaWR3IgpjdXQgPC0gMzAKZGF0X21lcmdlZDEgPC0gZGF0X21lcmdlZDAgJT4lIAogIGZpbHRlcihhZ2dfbWV0aG9kID09IGFnZywgY3V0b2ZmID09IGN1dCkgJT4lIAogIGRyb3BfbmEoaGVpZ2h0KQpkZnBtIDwtIGRhdF9tZXJnZWQxICU+JSAKICBncm91cF9ieShkYXRlLCBpZF9lcGEpICU+JSAKICBzdW1tYXJpemUocG0yNSA9IG1lYW4ocG0yNSkpICU+JSAKICB1bmdyb3VwKCkKZGF0X21lcmdlZDEgPC0gZGF0X21lcmdlZDEgJT4lIAogIHNlbGVjdCgtcG0yNSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIGxlZnRfam9pbihkZnBtKQoKYGBgCgpgYGB7cn0KZGYgPC0gZGF0X21lcmdlZDEKbWRsMCA8LSBmZW9scyhwbTI1IH4gYW90X2Fub20gKyBzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXkgfCBpZF9lcGEgKyBtb250aCwKICAgICAgICAgICAgICBkYXRhID0gZGYpCm1kbDEgPC0gZmVvbHMocG0yNSB+IGFvdF9hbm9tICsgc21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5ICsgYW90X2Fub206c21va2VfZGF5OmhlaWdodCB8IGlkX2VwYSArIG1vbnRoLAogICAgICAgICAgICAgIGRhdGEgPSBkZikKbWRsMiA8LSBmZW9scyhwbTI1IH4gYW90X2Fub20gKyBzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXkgKyBhb3RfYW5vbTpzbW9rZV9kYXk6cHJlc3N1cmUgfCBpZF9lcGEgKyBtb250aCwKICAgICAgICAgICAgICBkYXRhID0gZGYpCmBgYAoKYGBge3J9CmV0YWJsZShtZGwwLCBtZGwxLCBtZGwyLCBkcm9wLnNlY3Rpb24gPSAiZml4ZWYiKQpgYGAKCkRvZXNuJ3QgbG9vayBtdWNoIGRpZmZlcmVudCBmcm9tIGFib3ZlLgoKCg==